//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <winhack.h>
#include "framebuffer.h"
#include "sharemem.h"
#include "cursor_data.h"
#include <aura.h>
#define SDLPIXELBITS 32
//#define SDLPIXELBITS 16
#define CAPTION_MAXLENGTH               100
#define DEFAULT_CAPTION  L"Elastos Emulator(Default Mobile)"

CFrameBuffer::CFrameBuffer(Int32 posX,
    Int32 posY,
    Int32 width,
    Int32 height,
    Int32 colorFormat)
{
    m_drawSurfaceInterval = 40;

    m_mobilePosX = posX;
    m_mobilePosY = posY;
    m_mobileWidth = width;
    m_mobileHeight = height;

    m_screenPosX = 0;
    m_screenPosY = 0;
    m_screenWidth = width;
    m_screenHeight = height;

    m_bIsOverlay = FALSE;
    m_bIsSetOverlay1 = FALSE;
    m_bIsSetOverlay2 = FALSE;
    m_bIsUnderlay = FALSE;

    m_bShowLCDOnly = FALSE;

    SetBasePlaneColorFormat((ColorFormat)colorFormat);

    m_bRender = FALSE;
    m_bAutoRender = FALSE;

    m_pSurface = NULL;
    //unused for cursor in sandalwood.
    //m_pCursor  = NULL;
    m_timerId = NULL;
}

EXTERN bool g_isSimulator;
EXTERN BYTE* g_sharedMemoryAddress;
EXTERN Aura_Handle g_sharedmemory;

CFrameBuffer::~CFrameBuffer()
{
    if (g_isSimulator) {
        AuraSharedMemory_Detach(g_sharedmemory, (Aura_Address)g_sharedMemoryAddress);
        AuraSharedMemory_Destroy(g_sharedmemory);
    }

    if (NULL != m_pTransArray) {
        free(m_pTransArray);
        m_pTransArray = NULL;
    }
    if (NULL != m_bpBufferAddress) {
        FreeAllocShareMem((Address)m_bpBufferAddress);
        m_bpBufferAddress = NULL;
    }

    if (NULL != m_timerId) {
        SDL_RemoveTimer(m_timerId);
        m_timerId = NULL;
    }
    SDL_Quit();
}

extern int InitShell(char * caption, int x, int y, int width, int height);

ECode CFrameBuffer::Initialize()
{
    //char szCaption[CAPTION_MAXLENGTH + 1];
    //wchar_t esbCaption[CAPTION_MAXLENGTH];
    //if (NOERROR != ConfigGetItemStr(L"Caption", esbCaption)) {
    //    wcscpy(esbCaption, DEFAULT_CAPTION);
    //}

    ECode ec = NOERROR;

    ec = InitShell(NULL,
                  m_mobilePosX, m_mobilePosY,
                  m_mobileWidth, m_mobileHeight);
    if (FAILED(ec)) {
        return ec;
    }
    UInt32 flags = (SDL_INIT_VIDEO
                   | SDL_INIT_NOPARACHUTE);
    if (g_isSimulator)
        flags = flags | SDL_INIT_TIMER;
    if (SDL_Init(flags) < 0) {
        // todo;
        return E_FAIL;
    }
    m_pSurface = SDL_SetVideoMode(m_mobileWidth,
                             m_mobileHeight,
                             SDLPIXELBITS,
                             SDL_HWSURFACE | SDL_DOUBLEBUF);
    if (NULL == m_pSurface) {
        goto ErrorExit;
    }

   // m_pCursor = SDL_CreateCursor(Cursor_Data,
   //                            Cursor_Mask, 32, 32, 1, 2);
   // if (NULL == m_pCursor) {
   //     goto ErrorExit;
   // }
   // SDL_SetCursor(m_pCursor);

    ec = InitializeBaseplane();
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    return NOERROR;

ErrorExit:
    SDL_Quit();
    return ec;
}

ECode CFrameBuffer::InitializeBaseplane()
{
    ECode ec = NOERROR;

    m_bpBufferSize = m_screenWidth
                   * m_screenHeight
                   * m_bpPixelBytes;
    ec = AllocShareMem(m_bpBufferSize,
                     MemoryProtection_RW,
                     (Address *)&m_bpBufferAddress,
                     (ULONG *)&m_bpBufferAddressId);
    if (FAILED(ec)) {
        return ec;
    }
    memset((void *)m_bpBufferAddress, 0, m_bpBufferSize);
    // StartDrawing;

    m_nTransArraySize = m_screenWidth * m_screenHeight;
    m_pTransArray = (BYTE *)malloc(m_nTransArraySize);
    if (NULL == m_pTransArray) {
        FreeAllocShareMem((Address)m_bpBufferAddress);
        m_bpBufferAddress = NULL;
        return E_OUT_OF_MEMORY;
    }
    memset((void *)m_pTransArray, 0, m_nTransArraySize);

    return ec;
}

static Uint32 SDLCALL Drawing(Uint32 interval, void * param)
{
//    static Int32 count = 0;
//    printf(" <Drawing> count = %d.\n:", ++count);

    CFrameBuffer * pFrameBuffer = (CFrameBuffer *)param;
    pFrameBuffer->UpdateToSurface();
    return interval;
}

ECode CFrameBuffer::StartDrawing()
{
    m_timerId = SDL_AddTimer(m_drawSurfaceInterval,
                           Drawing,
                          (void *)this);
    if (NULL == m_timerId) {
        // todo;
        return E_FAIL;
    }

    m_bRender = TRUE;
    m_bAutoRender = FALSE;

    return NOERROR;
}

void CFrameBuffer::ClearBaseplane()
{
    assert(NULL != m_bpBufferAddress);
    assert(m_bpBufferSize > 0);
    memset((void *)m_bpBufferAddress,
            0,
            m_bpBufferSize);
}

void CFrameBuffer::GetBaseplaneAddress(
    /* [out] */ Address * pAddress,
    /* [out] */ UInt32 * pAddressId)
{
    assert(NULL != pAddress);
    assert(NULL != pAddressId);
    assert(NULL != m_bpBufferAddress);

    *pAddress = (Address)m_bpBufferAddress;
    *pAddressId = m_bpBufferAddressId;
}

void CFrameBuffer::GetBaseplaneFormat(
    /* [out] */ FrameBufferFormat * pFormat)
{
    assert(NULL != pFormat);

    pFormat->Color = m_bpColorFormat;
    pFormat->Bpp = m_bpPixelBits;
    pFormat->Width = m_screenWidth;
    pFormat->Height = m_screenHeight;
}

void CFrameBuffer::GetOverlayFormat(
    /* [in] */ Int32 layer,
    /* [out] */ OverlayFormat * pFormat)
{
    assert(NULL != pFormat);

    switch (layer) {
        case 1:
            *pFormat = m_overlay1;
            break;
        case 2:
            *pFormat = m_overlay2;
            break;
        default:
            assert(0);
            break;
    }
    return;
}

#if 0
Boolean CFrameBuffer::SetOverlay(OverlayFormat *pOverlayFormat)
{
    if (pOverlayFormat->Enable) {
        if (pOverlayFormat->Layer == 2) {
            if (m_bIsSetOverlay2) {
                m_bIsSetOverlay2 = FALSE;
                Sleep(DRAWSURFACETIMER);
            }
            switch (pOverlayFormat->Color) {
                case COLOR_YCbCr422:
                    if(!SetOverlayYCbCr422(pOverlayFormat)) goto ERR;
                    break;
                case COLOR_YCbCr420:
                    if(!SetOverlayYCbCr420(pOverlayFormat)) goto ERR;
                    break;
                default:
                    goto ERR;
            }
            m_bIsSetOverlay2 = TRUE;
        }
        if (1 == pOverlayFormat->Layer) {
            if (m_bIsSetOverlay1) {
                m_bIsSetOverlay1 = FALSE;
                Sleep(DRAWSURFACETIMER);
            }
            memcpy(&m_overlay1, pOverlayFormat, sizeof(OverlayFormat));
            SetOverlay1ColorFormat(m_overlay1.Color);

            //m_pOverlay1Fb =
            //    realloc(m_pOverlay2Fb, m_nOverlay1PixelBytes*m_overlay1.Width*m_overlay1.Height);
            //if (!m_pOverlay1Fb) goto ERR;
            ECode ec = AllocShareMem(m_nOverlay1PixelBytes*m_overlay1.Width*m_overlay1.Height,
                    MemoryProtection_RW,
                    (Address *)&m_pOverlay1Fb, NULL);
            if (NOERROR != ec) return FALSE;
            memset(m_pOverlay1Fb, 0,
                (m_nOverlay1PixelBytes*m_overlay1.Width*m_overlay1.Height));
            m_overlay1.pChannelFb[0] = m_pOverlay1Fb;
            m_overlay1.ChannelSize[0] =
                m_nOverlay1PixelBytes*m_overlay1.Width*m_overlay1.Height;
            m_bIsSetOverlay1 = TRUE;
        }
        if (m_bIsSetOverlay1 && m_bIsSetOverlay2) {
            m_bIsOverlay = GetTheoverlapArea();
            if (m_bIsUnderlay) {
                if (COLOR_RGB565 == m_overlay1.Color) {
                    m_OverlapAreaOperation = Underlay_OverlapAreaOperation_Overlay1RGB565;
                }
                else {
                    m_OverlapAreaOperation = Underlay_OverlapAreaOperation_Overlay1RGBT666;
                }
            }
        }
    }
    else {
        Video_DisableOverlay(pOverlayFormat->Layer);
    }

    printf("m_overlay2.pChannelFb[0]=%x\n", m_overlay2.pChannelFb[0]);

    return TRUE;

ERR:
    if (m_bIsUnderlay &&
        (COLOR_RGB565 == m_bpColorFormat)) {
        SetBasePlaneColorFormat(COLOR_RGB565);
    }

    return FALSE;
}

void CFrameBuffer::DisableOverlay(
    /* [in] */ Int32 layer)
{
    if ((layer&0x2) && (m_pOverlay2Fb != NULL)) {
        m_bIsSetOverlay2 = FALSE;
        Sleep(DRAWSURFACETIMER);
        //free(m_pOverlay2Fb);
        FreeAllocShareMem((Address)m_pOverlay2Fb);
        m_pOverlay2Fb = NULL;
        m_pOverlay2BufferCopy = NULL;
    }

    if ((layer&0x1) && (m_pOverlay1Fb != NULL)) {
        m_bIsSetOverlay1 = FALSE;
        Sleep(DRAWSURFACETIMER);
        //free(m_pOverlay1Fb);
        FreeAllocShareMem((Address)m_pOverlay1Fb);
        m_pOverlay1Fb = NULL;
        m_pOverlay1BufferCopy = NULL;
    }

    if ((!m_bIsSetOverlay1) || (!m_bIsSetOverlay2)) {
        m_bIsOverlay = FALSE;
        m_OverlapAreaOperation = NULL;
    }

    if ((!m_bIsSetOverlay1) && (!m_bIsSetOverlay2)) {
        if ((COLOR_RGBT555 == m_bpColorFormat)) {
            SetBasePlaneColorFormat(COLOR_RGB565);
        }
    }
}

void CFrameBuffer::SetTransparentLevel(
    /* [in] */ Int32 level)
{
    m_nTransparencyKey = level / 32; // [0, 255] -> [0, 7]
}
#endif

void CFrameBuffer::UpdateToSurface()
{
    SDL_LockSurface(m_pSurface);
    
    assert(NULL != m_bpBufferCopyFunc);
    (this->*m_bpBufferCopyFunc)(m_screenPosX,m_screenPosY,m_screenWidth,m_screenHeight);

    SDL_UpdateRect(m_pSurface,
                   m_screenPosX,
                   m_screenPosY,
                   m_screenWidth,
                   m_screenHeight);
    SDL_UnlockSurface(m_pSurface);
}

void CFrameBuffer::UpdateToSurface(Int32 x,Int32 y,Int32 width,Int32 height)
{
    SDL_LockSurface(m_pSurface);
   // m_bRender = FALSE;

    assert(NULL != m_bpBufferCopyFunc);
    (this->*m_bpBufferCopyFunc)(x,y,width,height);

    //{
    //    #include <stdio.h>
    //    int static count = 0;
    //    printf(" ##### <update> ### count :%d.\n", ++count);
    //}

/*
    if(m_bIsSetOverlay2) {
        assert(NULL != m_p);
        assert(NULL != g_videoCtx.pOverlay2BufferCopy);
        g_videoCtx.pOverlay2BufferCopy();
    }

    if(m_bIsOverlay && m_bIsUnderlay) {
        assert(NULL != g_videoCtx.OverlapAreaOperation);
        g_videoCtx.OverlapAreaOperation();
    }

    if(m_bIsSetOverlay1) {
        assert(NULL != g_videoCtx.pOverlay1BufferCopy);
        g_videoCtx.pOverlay1BufferCopy();
    }
*/
    SDL_UpdateRect(m_pSurface,
                   m_screenPosX+x,
                   m_screenPosY+y,
                   width,
                   height);
    SDL_UnlockSurface(m_pSurface);
}
